#include "../glutil/GLee.h"
#include <QtGui>
#include <QtOpenGL>

#include <math.h>
#include <iostream>
#include <fstream>
#include "glwidget.h"
#include "../dataengine/VorestHtmlReader.h"
#include "../glutil/GLSupport.h"
#include "../glutil/QGLBindTexture.h"
GLWidget::GLWidget(QWidget *parent) : QGLWidget(parent)
{
    rotx=-M_PI/4;
    roty=M_PI/8;
    selectedTree = 0;
    camDist = 2.0;
	m_AOQuality = 0;
	m_octreeRes = 5;
}


GLWidget::~GLWidget()
{
	delete m_mainShader;
	delete grass;
	delete ground;
}

void GLWidget::setGLLight()
{
    float ambientLight0[4] = {0.1,0.1,0.1,1.0f};
    float diffuseLight0[4] = {0.5,0.5,0.5,1.0f};
    float specularLight0[4] = {0.0,0.0,0.0,1.0f};
    float lightPosition0 [4] = {2, -4, 2,1.0};

	if(!(glutil::GLSupport::areFBOsSupported() && glutil::GLSupport::areShadersSupported()))
	{
		const float NoShadersLightFactor = 3.0f;
		for(int i=0;i<3;++i)
		{
			ambientLight0[i]*=NoShadersLightFactor;
			diffuseLight0[i]*=NoShadersLightFactor;
			specularLight0[i]*=NoShadersLightFactor;
		}
	}

    lightPosition0[0] = 20*sin(lightAngle);
	lightPosition0[1] = 1.0;
    lightPosition0[2] = 20*cos(lightAngle);

    glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight0);
    glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight0);
    glLightfv(GL_LIGHT0,GL_SPECULAR,specularLight0);
    glLightfv(GL_LIGHT0,GL_POSITION,lightPosition0);

    glEnable(GL_LIGHT0);
}

void GLWidget::initializeGL()
{
    makeCurrent();
	glClearColor(1.0,0.93,0.93,1.0);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);

    GLfloat ambientLight[]={0.4,0.35,0.35,1.0};
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambientLight);

    glShadeModel(GL_SMOOTH);
    QPixmap q = QPixmap(QString("data/ground.jpg"));
    QPixmap q2 = QPixmap(QString("data/shortGrass.jpg"));

	groundTexture = glutil::QGLBindTexture( q);
	groundTexture2 = glutil::QGLBindTexture (q2);
	skyBrightness = 1.0;

	m_mainFBO.init(800,600,GL_RGBA16F_ARB,false);
	m_hblurFBO.init(800,600,GL_RGBA16F_ARB,false);
	m_vblurFBO.init(800,600,GL_RGBA16F_ARB,false);
	
	m_hblurShader= new glutil::ShaderProgram();
	m_hblurShader->attachShaderFromFile(GL_VERTEX_SHADER, "data/main.vert");
	m_hblurShader->attachShaderFromFile(GL_FRAGMENT_SHADER, "data/hblur.frag");
	m_hblurShader->link();

	m_hblurShader->setUniform1i("mainTexture",0);
	m_hblurShader->setUniform1i("bloomSamples",5);
	m_hblurShader->setUniform1f("bloomDistance",5);
	m_hblurShader->setUniform1f("blurMean",0.7);
	m_hblurShader->setUniform2f("dispV",1.0,0.0);
	
	m_vblurShader= new glutil::ShaderProgram();
	m_vblurShader->attachShaderFromFile(GL_VERTEX_SHADER, "data/main.vert");
	m_vblurShader->attachShaderFromFile(GL_FRAGMENT_SHADER, "data/hblur.frag");
	m_vblurShader->link();

	m_vblurShader->setUniform1i("mainTexture",0);
	m_vblurShader->setUniform1f("bloomDistance",5);
	m_vblurShader->setUniform1i("bloomSamples",5);
	m_vblurShader->setUniform1f("blurMean",0.7);
	m_vblurShader->setUniform2f("dispV",0.0,1.0);
	
	m_mainShader = new glutil::ShaderProgram();
	m_mainShader->attachShaderFromFile(GL_VERTEX_SHADER, "data/main.vert");
	m_mainShader->attachShaderFromFile(GL_FRAGMENT_SHADER, "data/main.frag");
	m_mainShader->link();

	m_mainShader->setUniform1f("exposure",3.0f);
	m_mainShader->setUniform1f("gamma",0.0f);
	m_mainShader->setUniform1f("contrast",3.0f);
	m_mainShader->setUniform1i("mainTexture",0);
	m_mainShader->setUniform1i("blurTexture",1);
	m_mainShader->setUniform1i("bloomLevels",4);

	m_raysShader = new glutil::ShaderProgram();
	m_raysShader->attachShaderFromFile(GL_VERTEX_SHADER, "data/main.vert");
	m_raysShader->attachShaderFromFile(GL_FRAGMENT_SHADER, "data/rays.frag");
	m_raysShader->link();
	m_raysShader->setUniform1i("mainTexture",0);

	m_sky.initialize();
	ground = new Ground();
	grass = new Grass();
    QPixmap q3 = QPixmap(QString("data/grassShort.png"));
    QPixmap q4 = QPixmap(QString("data/flor1.png"));
    QPixmap q5 = QPixmap(QString("data/arbushto1.png"));
	grass->addGrassType(glutil::QGLBindTexture(q3), q3.width(), q3.height(), 1.0,0.4);
	grass->addGrassType(glutil::QGLBindTexture(q4), q4.width(), q4.height(), 0.02,0.4);
	grass->addGrassType(glutil::QGLBindTexture(q5), q5.width(), q5.height(), 0.01,0.5);


	m_finalPass = new glutil::EffectsPass2to1(m_mainShader, &m_mainFBO, &m_vblurFBO, NULL);
	m_hblurPass= new glutil::EffectsPass1to1(m_hblurShader, &m_mainFBO,&m_hblurFBO);
	m_vblurPass= new glutil::EffectsPass1to1(m_vblurShader, &m_hblurFBO,&m_vblurFBO);
	m_raysPass = new glutil::EffectsPass1to1(m_raysShader, &m_mainFBO, &m_hblurFBO);
}

void GLWidget::paintGL()
{
    makeCurrent();
	if(glutil::GLSupport::areFBOsSupported() && glutil::GLSupport::areShadersSupported())
	{
		m_mainFBO.beginCapture();
		m_mainFBO.bind();
	}
	glViewport(0, 0, widgetWidth, widgetHeight);
    glClear(GL_DEPTH_BUFFER_BIT);//(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
	set2DCamera();
    glDisable(GL_LIGHTING);
	glDepthMask(GL_FALSE);
 
    float camX,camY,camZ;
    camX = camDist*cos(rotx);
    camZ = camDist*sin(rotx);
    camY = roty;

	m_sky.draw(geom::Vector3D(-camX,-camY,-camZ), M_PI/4, M_PI/4*widgetWidth/widgetHeight);
	glDepthMask(GL_TRUE);
	glDisable(GL_LIGHTING);
    
    setCamera();
	
	gluLookAt (camX, camY, camZ, 0, camY/2, 0, 0, 1, 0);

    setGLLight();
    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);

    //glEnable(GL_DEPTH_TEST);
	{
		glEnable(GL_LIGHTING);

		glActiveTexture(GL_TEXTURE0);
	    glBindTexture(GL_TEXTURE_2D,groundTexture);
   		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

    	glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_MIRRORED_REPEAT);
    	glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_MIRRORED_REPEAT);

		glActiveTexture(GL_TEXTURE1);
	    glBindTexture(GL_TEXTURE_2D,groundTexture2);
   		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
   		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

		glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_MIRRORED_REPEAT);
		glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_MIRRORED_REPEAT);


		ground->draw(1.0); // 1.0->opacity

		glBindTexture(GL_TEXTURE_2D,0);
		glActiveTexture(GL_TEXTURE0);
    	glDisable(GL_TEXTURE_2D);
    	glBindTexture(GL_TEXTURE_2D,0);
		glDisable(GL_LIGHTING);

	}
	POVGeom::POVObject * tree; 

    if((int)trees.size()<=selectedTree)
        tree = NULL;
    else
		tree = trees.at(selectedTree);

	glColor4f(0.0,0.0,0.0,1.0);
	
    if(tree!=NULL)
	{
		if(m_renderer.get() == NULL)
			m_renderer.reset(glutil::ModelRenderer::simpleCenteredInstanceCreator(*tree));//r);

		m_renderer->renderAll(true);
	}
	grass->draw(widgetWidth);

	if(glutil::GLSupport::areFBOsSupported() && glutil::GLSupport::areShadersSupported())
	{
		m_mainFBO.endCapture();

		m_hblurPass->doPass(widgetWidth/2,widgetHeight);
		m_vblurPass->doPass(widgetWidth/2,widgetHeight/2);
		//m_raysPass->doPass(widgetWidth/2,widgetHeight);
		m_finalPass->doPass(widgetWidth,widgetHeight);
	}
}

void GLWidget::set2DCamera()
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    //glOrtho(0,10,0,10,0.1,100.0);
	glOrtho(-1,1,-1,1,0.1,100.0);

    glMatrixMode(GL_MODELVIEW);
}

void GLWidget::setCamera()
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    float nearPlaneDistance = 0.1;
    float farPlaneDistance = 1000.0;

    gluPerspective (45, widgetWidth/(float)widgetHeight, nearPlaneDistance, farPlaneDistance);

    glMatrixMode(GL_MODELVIEW);
}

void GLWidget::resizeGL(int width, int height)
{
    widgetWidth = width;
    widgetHeight = height;
	m_mainFBO.release();
	m_mainFBO.init(width,height,GL_RGBA16F_ARB, false);

	m_hblurFBO.release();
	m_hblurFBO.init(width/2,height,GL_RGBA16F_ARB, false);
	m_vblurFBO.release();
	m_vblurFBO.init(width/2,height/2,GL_RGBA16F_ARB, false);
	
	m_mainShader->setUniform1f("windowWidth", width);
	m_mainShader->setUniform1f("windowHeight", height);

	m_hblurShader->setUniform1f("textureSize", width/2);
	m_vblurShader->setUniform1f("textureSize", height/2);
}

void GLWidget::wheelEvent(QWheelEvent * event)
{
    camDist *= (1-event->delta()/3000.0);
    if(camDist<0.1)
        camDist=0.1;;
    if(camDist>200)
        camDist = 200;
    updateGL();
}

void GLWidget::mousePressEvent(QMouseEvent *event)
{
    lastPos = event->pos();
}

void GLWidget::keyPressEvent ( QKeyEvent * event )
{
	event=event; // To avoid warnings of unused variable
    lightAngle+=0.1;
    updateGL();
}

void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
    int dx = event->x() - lastPos.x();
    int dy = event->y() - lastPos.y();

    rotx+= dx/180.0;
    roty-= dy/180.0;
	if(roty<0.1)
		roty=0.1;
	if(roty>100)//-M_PI/2)
		roty=100;//-M_PI/2;
    lastPos = event->pos();
    updateGL();
}

void GLWidget::update3DModel()
{
	m_renderer.reset();
}
